package com.mx.adapt.anytype

import android.content.Context
import android.view.ViewGroup
import android.widget.Space
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.mx.adapt.MXBaseViewHolder

/**
 * @author 张孟雄
 *
 * 任意类型的适配器代理管理器
 *
 */
class MXLayoutManager(context: Context) :
    GridLayoutManager(context, 1, VERTICAL, false) {
    private var recyclerView: RecyclerView? = null
    private val adaptList = ArrayList<MXBaseTypeAdapt<*>>()
    private val adaptDataObserverMap = HashMap<Long, TypeObserver>() // 每个Adapt对应的观察者对象

    init {
        // 核心功能，通过调整SpanSize来控制每个Item需要占用一行中的几个位置
        super.setSpanSizeLookup(object : SpanSizeLookup() {
            override fun getSpanSize(position: Int): Int {
                val spanSize = getAdaptByPosition(position)?.first?.getSpanSize()
                    ?: return spanCount
                return spanCount / spanSize
            }
        })
    }

    /**
     * 屏蔽该功能，不能修改
     */
    override fun setSpanSizeLookup(spanSizeLookup: SpanSizeLookup?) {
        //super.setSpanSizeLookup(spanSizeLookup)
    }

    override fun setSpanCount(spanCount: Int) {
//        super.setSpanCount(spanCount)
    }

    /**
     * 根据总位置获取对应的Item所在的Adapt
     */
    private fun getAdaptByPosition(position: Int): Pair<MXBaseTypeAdapt<*>, Int>? {
        var start = 0
        var a: MXBaseTypeAdapt<*>? = null
        var p = 0
        for (adapt in adaptList) {
            val size = adapt.itemCount
            if (position >= start && position < size + start) {
                a = adapt
                p = position - start
                break
            } else {
                start += size
            }
        }
        if (a != null) {
            return Pair(a, p)
        }
        return null
    }

    /**
     * 绑定RecycleView
     */
    fun attachToRecycleView(recyclerView: RecyclerView) {
        this.recyclerView = recyclerView
        recyclerView.layoutManager = this
        delegateAdapt.setHasStableIds(true)
        recyclerView.adapter = delegateAdapt
    }

    /**
     * 设置适配器列表
     */
    fun setAdapts(list: List<MXBaseTypeAdapt<*>>) {
        cleanAllAdapt()
        adaptList.addAll(list)
        adaptList.forEach { adapt ->
            val observer = TypeObserver(this, adapt = adapt)
            adapt.registerAdapterDataObserver(observer)
            adaptDataObserverMap[adapt.getAdaptIndex()] = observer
        }
        notifyDataSetChanged()
    }

    /**
     * 清理适配器
     */
    fun cleanAllAdapt() {
        adaptDataObserverMap.values.forEach {
            it.adapt.unregisterAdapterDataObserver(it)
        }
        adaptDataObserverMap.clear()
        adaptList.clear()
        delegateAdapt.notifyDataSetChanged()
    }

    /**
     *
     */
    fun notifyDataSetChanged() {
        val spans = ArrayList<Int>()
        adaptList.toList().forEach { adapt ->
            val span = adapt.getSpanSize()
            if (span <= 0) {
                throw UnknownError("adapt span size not valid")
            }
            if (!spans.contains(span)) {
                spans.add(span)
            }
        }
        val exc = ArrayList<Int>()
        for (x in spans) {
            for (y in spans) {
                if (x == y) continue
                if (x > y && x % y == 0) {
                    exc.add(y) // 剔除可倍除的span，减小乘积
                }
            }
        }
        spans.removeAll(exc.distinct())
        var sp = 1
        spans.forEach {
            sp *= it
        }

        super.setSpanCount(sp)
        delegateAdapt.notifyDataSetChanged()
    }

    /**
     * 适配器总代理
     *
     */
    private val delegateAdapt = object : RecyclerView.Adapter<MXBaseViewHolder>() {
        override fun getItemCount(): Int {
            return adaptList.toList().sumOf { it.itemCount }
        }

        override fun getItemId(position: Int): Long {
            return position.toLong()
        }

        override fun getItemViewType(position: Int): Int {
            return getAdaptByPosition(position)?.first?.getAdaptViewType() ?: VIEW_TYPE_NONE
        }

        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MXBaseViewHolder {
            if (viewType == VIEW_TYPE_NONE) return MXBaseViewHolder({ Space(context) }, viewType)
            val holder = adaptList.toList().firstOrNull { it.getAdaptViewType() == viewType }
                ?.createViewHolder(parent, viewType)
            return holder ?: MXBaseViewHolder({ Space(context) }, viewType)
        }

        override fun onBindViewHolder(holder: MXBaseViewHolder, position: Int) {
            if (holder.type == VIEW_TYPE_NONE) return
            val pair = getAdaptByPosition(position) ?: return
            val index = pair.second
            pair.first.onBindViewHolder(holder, index)
        }
    }

    private class TypeObserver(
        val manager: MXLayoutManager,
        val adapt: MXBaseTypeAdapt<*>
    ) : RecyclerView.AdapterDataObserver() {
        override fun onChanged() {
            manager.delegateAdapt.notifyDataSetChanged()
        }

        override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
            manager.delegateAdapt.notifyItemRangeRemoved(
                getStartIndexInAllAdapt() + positionStart,
                itemCount
            )
        }

        override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
            manager.delegateAdapt.notifyDataSetChanged()
        }

        override fun onItemRangeChanged(positionStart: Int, itemCount: Int) {
            manager.delegateAdapt.notifyItemRangeChanged(
                getStartIndexInAllAdapt() + positionStart,
                itemCount
            )
        }

        private fun getStartIndexInAllAdapt(): Int {
            val index = adapt.getAdaptIndex()
            var start = 0
            for (adapt in manager.adaptList.toList()) {
                if (adapt.getAdaptIndex() == index) break
                start += adapt.itemCount
            }
            return start
        }
    }

    companion object {
        const val VIEW_TYPE_NONE = -10000
    }
}